package com.tankGame;

import java.awt.*;
import java.util.Vector;

/**
 * @author LingLed
 * @version 1.0
 * Tank类,记录坐标
 */
public class Tank {
    private int x, y;
    private final int defaultX, defaultY,defaultDirect;
    //初始位置坐标
    private int direct = 0;
    private int type = 1;
    private int speed = 1;
    public static final int DIRECT_UP = 0;
    public static final int DIRECT_DOWN = 1;
    public static final int DIRECT_LEFT = 2;
    public static final int DIRECT_RIGHT = 3;
    public static final int TYPE_PLAYER = 0;
    public static final int TYPE_ENEMY = 1;
    public static boolean protectType = true;
    //阵营保护
    public static final Vector<Tank.Bullet> bullets = new Vector<>();
    public static final Vector<Tank> tanks = new Vector<>();

    public int getDefaultY() {
        return defaultY;
    }
    public int getDefaultX() {
        return defaultX;
    }
    public int getDefaultDirect() {
        return defaultDirect;
    }

    /**
     * 坦克的子弹类
     * 成员内部类
     * 可以直接访问外部类的成员
     * 所以直接实例化该类时就直接记录实例化时的坦克状态,以制定炮弹的坐标和方向
     * 需要发射炮弹时直接实例化该类即可,
     * 不需要再通过外部类来记录坦克状态,
     * 绘制炮弹是绘制bullets集合内的炮弹,所以需要将实例化且未销毁的炮弹添加到集合中
     * 外部类fire()方法打包了这一过程,
     * 所以外部类只需要调用fire()方法即可
     */
    class Bullet {
        //炮弹,发射时进行实例化,每个炮弹都是一个对象
        private Tank t = Tank.this;
        private int x;
        private int y;
        //炮弹中心位置
        private final int direct;
        private int speed = 3;

        //记录发射瞬间坦克朝向
        public Bullet() {
            x = t.x + 20;
            y = t.y + 30;
            //获取坦克中心点位,炮管长度30像素点
            direct = t.direct;
            //获取坦克当前朝向
            switch (direct) {
                //根据朝向决定子弹中心位置
                case Tank.DIRECT_UP:
                    y -= 30;
                    break;
                case Tank.DIRECT_DOWN:
                    y += 30;
                    break;
                case Tank.DIRECT_RIGHT:
                    x += 30;
                    break;
                case Tank.DIRECT_LEFT:
                    x -= 30;
            }


            move();
            //子弹创建时立马启动ai
        }

        public void draw(Graphics g) {
            if (g == null) {
                throw new RuntimeException("画笔类缺失");
            }
            switch (type) {
                case 0:
                    g.setColor(Color.cyan);
                    break;
                case 1:
                    g.setColor(Color.yellow);
                    break;
                default:
                    throw new RuntimeException("shell type not exist");
            }//炮弹与坦克同色

            g.fillOval(x - 5, y - 5, 10, 10);
            //画炮弹
        }

        private boolean isLive = true;

        public void move() {
            //炮弹移动,负责修改炮弹坐标与检测出边界
            //创建新线程,根据更改炮弹坐标,重绘实现移动
            Thread thread = new Thread() {
                @Override
                public void run() {
                    while (isLive) {
//                        System.out.println(x +  " " + y);
                        switch (direct) {
                            case Tank.DIRECT_UP:
                                y -= speed;
                                break;
                            case Tank.DIRECT_DOWN:
                                y += speed;
                                break;
                            case Tank.DIRECT_RIGHT:
                                x += speed;
                                break;
                            case Tank.DIRECT_LEFT:
                                x -= speed;
                                break;
                        }
                        hitTank();
                        hitWall();
//                        mp.repaint();
                        try {
                            Thread.sleep(1000 / 144);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (x <= 0 || MyPanel.WIDTH <= x || y <= 0 || MyPanel.HEIGHT <= y) {
                            //触碰边界检测
                            destruction();
//                            mp.repaint();
                            //从炮弹列表中移除自己,停止线程
//                            System.out.println(bullets.size());
                            break;
                        }
                    }
                }
            };
            thread.start();

        }


        public int getSpeed() {
            return speed;
        }

        public void setSpeed(int speed) {
            this.speed = speed;
        }

        /**
         * 子弹销毁
         */
        private void destruction() {
            t = null;
            bullets.remove(this);
            isLive = false;
        }

        private void hitTank() {
            //炮弹击中坦克
            //检测当前炮弹是否与任意坦克重叠,在子弹move时进行判定
            //炮弹中心坐标
            int bx = this.x;
            int by = this.y;
            for (int i = 0; i < tanks.size(); i++) {
                //遍历所有tanks,挨个判断
                Tank otherTank = tanks.get(i);
                if (otherTank == t) {
                    //排除与自己重叠的情况
                    continue;
                }
                if (protectType) {
                    //阵营保护判定
                    if (type == otherTank.getType()) {
                        continue;
                    }
                }
                //获取当前坦克方向
                int direct = otherTank.getDirect();
                //获取坦克中心坐标
                int x = otherTank.getX() + 20;
                int y = otherTank.getY() + 30;
                switch (direct) {
                    case Tank.DIRECT_UP:
                    case Tank.DIRECT_DOWN:
                        if (bx >= x - 20 && bx <= x + 20 && by >= y - 30 && by <= y + 30) {
                            //爆炸特效
                            new Boom(x - 30, y - 30, 60, 60);
                            //坦克被击中
                            otherTank.kill();
                            //移除tank
                            destruction();
                        }
                        break;
                    case Tank.DIRECT_RIGHT:
                    case Tank.DIRECT_LEFT:
                        if (bx >= x - 30 && bx <= x + 30 && by >= y - 20 && by <= y + 20) {
                            //爆炸特效
                            new Boom(x - 30, y - 30, 60, 60);
                            //坦克被击中
                            otherTank.kill();
                            //移除tank
                            destruction();
                        }
                        break;
                }
            }
        }

        private void hitWall() {
            //炮弹中心坐标
            int bx = this.x;
            int by = this.y;
            for (int i = 0; i < Wall.walls.size(); i++) {
                //获取墙对象
                Wall wall = Wall.walls.get(i);
                //获取墙的坐标
                int x = wall.getX();
                int y = wall.getY();
                int dx = wall.getX() + wall.getWidth();
                int dy = wall.getY() + wall.getHeight();
                if (bx >= x && bx <= dx && by >= y && by <= dy) {
                    //新建爆炸对象
                    new Boom(x, y, wall.getWidth(), wall.getHeight());
                    //炮弹中心坐标与wall重叠
                    wall.destroy();
                    //墙体销毁,炮弹销毁
                    destruction();
                }
            }
        }
    }

    static class Boom {
        //用于实现爆炸效果的类
        public static final Vector<Boom> booms = new Vector<>();
        private static final Image BOOM1 = Toolkit.getDefaultToolkit().getImage("out/production/Code15/Boom1.png");//大
        private static final Image BOOM2 = Toolkit.getDefaultToolkit().getImage("out/production/Code15/Boom2.png");//中
        private static final Image BOOM3 = Toolkit.getDefaultToolkit().getImage("out/production/Code15/Boom3.png");//小
        private int life = 3;
        //根据lift数值决定画哪张图片
        private int x, y, weight, hight;
        private int speed = 100;
        //图片刷新速度
        private Image boom = BOOM1;

        /**
         * 爆炸对象,触发爆炸时直接实例化对象,输入坐标,图片大小
         *
         * @param x
         * @param y
         * @param weight
         * @param hight
         */
        public Boom(int x, int y, int weight, int hight) {
            this.x = x;
            this.y = y;
            this.weight = weight;
            this.hight = hight;
            booms.add(this);
            refresh();
        }

        public void draw(Graphics g) {
            g.drawImage(boom, x, y, weight, hight, null);
        }

        private void refresh() {
            //图片刷新方法,使用新线程
            new Thread() {
                @Override
                public void run() {
                    while (life > 0) {
                        switch (life) {
                            case 3:
                                boom = BOOM1;
                                break;
                            case 2:
                                boom = BOOM2;
                                break;
                            case 1:
                                boom = BOOM3;
                                break;
                        }
                        life--;
                        try {
                            Thread.sleep(speed);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    kill();
                    //销毁对象
                }
            }.start();

        }

        private void kill() {
            //销毁对象
            booms.remove(this);
            boom = null;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

    private boolean moveUp = false;
    private boolean moveDown = false;
    private boolean moveRight = false;
    private boolean moveLeft = false;

    public void stopMove() {
        moveUp = false;
        moveDown = false;
        moveRight = false;
        moveLeft = false;
    }

    public void moveUp() {
        if (moveUp) {
            return;
        }
        Thread thread = new Thread() {
            @Override
            public void run() {
                stopMove();
                moveUp = true;
                while (moveUp) {
                    move(DIRECT_UP);
//                    mp.repaint();
                    try {
                        Thread.sleep(1000 / 144);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }

    public void stopMoveUp() {
        moveUp = false;
    }

    public void moveDown() {
        if (moveDown) {
            return;
        }
        Thread thread = new Thread() {
            @Override
            public void run() {
                stopMove();
                moveDown = true;
                while (moveDown) {
                    move(DIRECT_DOWN);
//                    mp.repaint();
                    try {
                        Thread.sleep(1000 / 144);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }

    public void stopMoveDown() {
        moveDown = false;
    }

    public void moveLeft() {
        if (moveLeft) {
            //防止重复输入
            return;
        }
        Thread thread = new Thread() {
            @Override
            public void run() {
                stopMove();
                moveLeft = true;
                while (moveLeft) {
                    move(DIRECT_LEFT);
//                    mp.repaint();
                    try {
                        Thread.sleep(1000 / 144);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }

    public void stopMoveLeft() {
        moveLeft = false;
    }

    public void moveRight() {
        if (moveRight) {
            return;
        }
        Thread thread = new Thread() {
            @Override
            public void run() {
                stopMove();
                moveRight = true;
                while (moveRight) {
                    move(DIRECT_RIGHT);
//                    mp.repaint();
                    try {
                        Thread.sleep(1000 / 144);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }

    public void stopMoveRight() {
        moveRight = false;
    }

    /**
     * 移动方法
     *
     * @param direct 移动方向
     */
    private void move(int direct) {
        if (direct == DIRECT_UP) {
            y -= speed;
            this.direct = DIRECT_UP;
        }
        if (direct == DIRECT_DOWN) {
            y += speed;
            this.direct = DIRECT_DOWN;
        }
        if (direct == DIRECT_LEFT) {
            x -= speed;
            this.direct = DIRECT_LEFT;
        }
        if (direct == DIRECT_RIGHT) {
            x += speed;
            this.direct = DIRECT_RIGHT;
        }
//        int x1 = x - 10;
//        int y1 = y + 10;
        //判断是否出界
        if (y < 0) {
            y = 0;
        }
        if (y + 60 > MyPanel.HEIGHT) {
            y = MyPanel.HEIGHT - 60;
        }
        //侧向时坦克坐标点应为x-10
        if (x - 10 < 0) {
            x = 10;
        }
        if (x - 10 + 60 > MyPanel.WIDTH) {
            x = MyPanel.WIDTH - 60 + 10;
        }
        //判断是否撞墙
        for (int i = 0; i < Wall.walls.size(); i++) {
            //获取墙对象
            Wall wall = Wall.walls.get(i);
            //获取墙的坐标
            int x1 = this.x - 10;
            int y1 = this.y + 10;
            int x = wall.getX();
            int y = wall.getY();
            int dx = wall.getX() + wall.getWidth();
            int dy = wall.getY() + wall.getHeight();
            switch (direct) {
                case DIRECT_UP:
                    if (((this.y <= dy && this.y >= y))
                            && ((this.x >= x && this.x <= dx) || (this.x + 40 >= x && this.x + 40 <= dx))) {
                        this.y = dy + 1;
                    }
                    if (this.y + 60 >= y && this.y + 60 <= dy
                            && ((this.x >= x && this.x <= dx) || (this.x + 40 >= x && this.x + 40 <= dx))) {
                        this.y = y - 60 - 1;
                    }
                    break;
                case DIRECT_DOWN:
                    if ((this.y + 60 >= y && this.y + 60 <= dy)
                            && ((this.x >= x && this.x <= dx) || (this.x + 40 >= x && this.x + 40 <= dx))) {
                        this.y = y - 60 - 1;
                    }
                    if (this.y <= dy && this.y >= y
                            && ((this.x >= x && this.x <= dx) || (this.x + 40 >= x && this.x + 40 <= dx))) {
                        this.y = dy + 1;
                    }
                    break;
                case DIRECT_LEFT:
                    if ((x1 <= dx && x1 >= x)
                            && ((y1 >= y && y1 <= dy) || (y1 + 40 >= y && y1 + 40 <= dy))) {
                        this.x = dx + 10 + 1;
                    }
                    if ((x1 + 60 >= x && x1 + 60 <= dx)
                            && ((y1 >= y && y1 <= dy) || (y1 + 40 >= y && y1 + 40 <= dy))) {
                        this.x = x - 50 - 1;
                    }
                    break;
                case DIRECT_RIGHT:
                    if ((x1 + 60 >= x && x1 + 60 <= dx)
                            && ((y1 >= y && y1 <= dy) || (y1 + 40 >= y && y1 + 40 <= dy))) {
                        this.x = x - 50 - 1;
                    }
                    if ((x1 <= dx && x1 >= x) && ((y1 >= y && y1 <= dy) || (y1 + 40 >= y && y1 + 40 <= dy))) {
                        this.x = dx + 10 + 1;
                    }
            }
        }
    }

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        this.direct = direct;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public int getSpeed() {
        return speed;
    }

    /**
     * 设置移动速度
     *
     * @param speed
     */
    public void setSpeed(int speed) {
        if (speed < 1) {
            throw new IllegalArgumentException("speed must be more than 0");
            //speed不能小于1
        }
        this.speed = speed;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public Tank(int x, int y, int type) {
        this.x = x;
        this.y = y;
        this.defaultX = x;
        this.defaultY = y;
        this.defaultDirect = direct;
        this.type = type;
    }

    //使用现有参数直接draw
    public void draw(Graphics g) {
        if (g == null) {
            throw new RuntimeException("g不能为空");
        }
        drawTank(getX(), getY(), g, getDirect(), getType());
    }

    /**
     * 画坦克
     *
     * @param x      坦克x坐标
     * @param y      坦克y坐标
     * @param g      画笔工具,提供绘画方法
     * @param direct 方向 0:上 1:下 2:左 3:右
     * @param type   种类 0:我方 1:敌方
     */
    public void drawTank(int x, int y, Graphics g, int direct, int type) {
        Color color = g.getColor();
        int x1 = x - 10;
        int y1 = y + 10;

        //判断type类型
        switch (type) {
            case 0:
                g.setColor(Color.cyan);
                break;
            case 1:
                g.setColor(Color.yellow);
                break;
            default:
                throw new RuntimeException("tank type not exist");
        }

        //判断direct朝向,根据朝向绘制坦克
        switch (direct) {
            case 0:
                //朝上
                g.fill3DRect(x, y, 10, 60, false);
                g.fill3DRect(x + 30, y, 10, 60, false);
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                g.fillOval(x + 10, y + 20, 20, 20);
                g.drawLine(x + 20, y + 30, x + 20, y);
                break;
            case 1:
                //朝下
                g.fill3DRect(x, y, 10, 60, false);
                g.fill3DRect(x + 30, y, 10, 60, false);
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                g.fillOval(x + 10, y + 20, 20, 20);
                g.drawLine(x + 20, y + 30, x + 20, y + 60);
                break;
            case 2:
                //朝左
                g.fill3DRect(x1, y1, 60, 10, false);
                g.fill3DRect(x1, y1 + 30, 60, 10, false);
                g.fill3DRect(x1 + 10, y1 + 10, 40, 20, false);
                g.fillOval(x1 + 20, y1 + 10, 20, 20);
                g.drawLine(x1 + 30, y1 + 20, x1, y1 + 20);
                break;
            case 3:
                //朝右
                g.fill3DRect(x1, y1, 60, 10, false);
                g.fill3DRect(x1, y1 + 30, 60, 10, false);
                g.fill3DRect(x1 + 10, y1 + 10, 40, 20, false);
                g.fillOval(x1 + 20, y1 + 10, 20, 20);
                g.drawLine(x1 + 30, y1 + 20, x1 + 60, y1 + 20);
                break;
            default:
                throw new RuntimeException("direct error");
        }
        g.setColor(color);
    }

    /**
     * 发射炮弹,需要发射炮弹时直接调用即可
     */
    public void fire() {
        bullets.add(new Bullet());
        //创建子弹时炮弹类会自动根据当前坦克实例的方向和坐标来实现炮弹效果,无需实参
        //创建后放进炮弹列表
    }

    public void kill() {
        //从集合列表清除,结束绘制
        tanks.remove(this);
        //暂停ai
        stop();
    }

    public void start() {
        randomMove();
        randomFire();
    }

    public void stop() {
        stopRandomFire();
        stopRandomMove();
    }


    private boolean randomFire = true;

    /**
     * 随机开火
     * 方法中新建了一个线程,随机延时循环开火
     * (int) (Math.random() * 1000 * 1.5)
     */
    public void randomFire() {
        //随机开火
        //延时发布随机命令
        new Thread() {
            @Override
            public void run() {
                while (randomFire) {
                    fire();
                    try {
                        Thread.sleep((int) (Math.random() * 1000 * 1.5));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }.start();
    }

    /**
     * 结束随机开火
     */
    public void stopRandomFire() {
        randomFire = false;
    }

    private boolean randomMove = true;

    /**
     * 随机行走
     * 方法中新建了一个线程,随机方向行驶
     * (int) (Math.random() * 1000 * 1)
     */
    public void randomMove() {
        new Thread() {
            @Override
            public void run() {
                while (randomMove) {
                    //随机行走
                    //设置方向随机
                    int direct = (int) (Math.random() * 5);
//                    System.out.println(direct);
                    switch (direct) {
                        case Tank.DIRECT_UP:
                            moveUp();
                            break;
                        case Tank.DIRECT_DOWN:
                            moveDown();
                            break;
                        case Tank.DIRECT_RIGHT:
                            moveRight();
                            break;
                        case Tank.DIRECT_LEFT:
                            moveLeft();
                            break;
                        default:
                            stopMove();
                            break;
                    }

                    //延时发布随机命令
                    try {
                        Thread.sleep((int) (Math.random() * 1000 * 1));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }.start();
    }

    /**
     * 结束随机行走
     */
    public void stopRandomMove() {
        randomMove = false;
    }
}